package waggle.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
//kayjean 20110309
//import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
//import com.google.gwt.maps.client.event.MapDragEndHandler;
//import com.google.gwt.maps.client.event.MapZoomEndHandler;
//import com.google.gwt.maps.client.event.MapDragEndHandler.MapDragEndEvent;
//import com.google.gwt.maps.client.event.MapZoomEndHandler.MapZoomEndEvent;


import waggle.client.bean.Occurrence;
import waggle.client.bean.User;
import waggle.client.DataPager.PageListener;
import waggle.client.OccurrenceQuery.ResultFilter;
import waggle.client.View.UrlParam;

/**
 * A view that supports searching occurrences and paging through results which
 * can be displayed as a map view, a list view, or a detail view.
 */
public class OccurrenceView extends ComponentView implements
//		MapDragEndHandler, MapZoomEndHandler,
		PageListener<Occurrence>, ClickHandler {
/*
//測試
	public void onDragEnd(MapDragEndEvent event) {
			//配合讓地圖移動影響結果 handleHistoryEvent();
			//地圖移動或是放大縮小,就要重第一頁開始計算
		currentPageNum = 1;
	}
	
	public void onZoomEnd(MapZoomEndEvent event) {
			//配合讓地圖移動影響結果 handleHistoryEvent();
			//地圖移動或是放大縮小,就要重第一頁開始計算
		currentPageNum = 1;
	}
*/	
	
	
	
	// 延續先前需要項目, resize
	@Override
	public void onResize(ResizeEvent event) {
		resize(event.getWidth(), event.getHeight());
	}

	@Override
	protected void resize(final int width, int height) {
		if (!isMyView(History.getToken())) {
			return;
		}
		toolHp.setWidth(width + "px");
		height = height - mainSp.getAbsoluteTop();
		if (height <= 0) {
			height = 1;
		}
		mainSp.setPixelSize(width, height);
//kayjean 20110309
/*		
		if (activeViewInfo != null) {
			getPopupView(activeViewInfo.getName()).reshow();
		}
*/		
		activeViewInfo.getView().setVisible(true);
		
		
		Window.enableScrolling(toolHp.getOffsetWidth() - 10 > width);

	}

//	kayjean 20110309
/*	
	// 下面這個部份是配合google earth?? 現在先全部COPY,未來因為我也沒有earth,或許可以避開
	public CustomPopupPanel getPopupView(String viewName) {
		viewName = viewName.toLowerCase();
		CustomPopupPanel popupView = viewsMap.get(viewName);
		if (popupView == null) {
			popupView = new CustomPopupPanel(viewInfos.get(viewName).getView());
			viewsMap.put(viewName, popupView);
		}
		return popupView;
	}
*/
	
//kayjean 20110309
//	private final Map<String, CustomPopupPanel> viewsMap = new HashMap<String, CustomPopupPanel>();

	/**
	 * Temporally solution for earth map type bug switching view bug. This
	 * extends PopupPanel to override {@link PopupPanel#hide(boolean)} to just
	 * set the {@link PopupPanel} invisible so that the widget is still attached
	 * to the DOM element but does not show it.
	 * 
	 * @author Tri
	 * 
	 */
//kayjean 20110309
/*	
	private class CustomPopupPanel extends PopupPanel {
		public CustomPopupPanel(View w) {
			super(false);
			setWidget(w);
			setStyleName(ComponentView.STYLE_NAME);
		}

		@Override
		public void hide(boolean autoClose) {
			setVisible(false);
			Widget view = getWidget();
			if (view instanceof MapView) {
				((MapView) view).setVisible(false);
			}

		}

		public void reshow() {
			show();
		}

		@Override
		public void show() {
			setPopupPosition(toolHp.getAbsoluteLeft(), toolHp.getAbsoluteTop()
					+ toolHp.getOffsetHeight());
			super.show();
			setVisible(true);
			View view = (View) getWidget();
			view.onShow();
			if (view instanceof MapView) {
				((MapView) view).setVisible(true);
			}
		}
	}
*/

	// 延續先前需要項目 history
	@Override
	protected boolean isMyView(String value) {
		if (value.trim().equals("") && isDefaultView()) {
			return true;
		}
		List<String> views = ApplicationView.getHistoryTokenParamValues(value,
				UrlParam.VIEW.lower());
		if (views == null || views.isEmpty()) {
			return false;
		}
		String view = views.get(0);
		for (ViewInfo info : viewInfos.values()) {
			if (info.getName().equalsIgnoreCase(view)) {
				return true;
			}
		}
		return false;
	}

	private final HistoryState historyState = new HistoryState() {

		@Override
		public Object getHistoryParameters(UrlParam param) {
			switch (param) {
			case VIEW:
			case QUERY:
			case TYPE:
			case ST:
			case ERROR_TYPE:
				return stringValue(param);
			case PAGE:
				return integerValue(param);
			case RF:
				ResultFilter resultFilter = ResultFilter.PUBLIC;
				String resultFilterToken = stringValue(param);
				if (resultFilterToken.equalsIgnoreCase("both")) {
					resultFilter = ResultFilter.BOTH;
				} else if (resultFilterToken.equalsIgnoreCase("private")) {
					resultFilter = ResultFilter.PRIVATE;
				}
				return resultFilter;
			case ASEARCH:
				return listValue(param);

			//配合地圖調整新加入
			case SOUTHWEST:
				return latLngBoundsValue(param);
			case NORTHEAST:
				return latLngBoundsValue(param);
				
			}
			return "";
		}

	};

	private static final String DEFAULT_VIEW = MAP;

	@Override
	protected void handleOnValueChange(String historyToken) {
		historyState.setHistoryToken(historyToken);
		String view = historyState.getHistoryParameters(UrlParam.VIEW) + "";
		if (view.equals("")) {
			view = DEFAULT_VIEW;
		}
		if (ApplicationView.getCurrentState().equals(ViewState.UNAUTHENTICATED)
				&& view.equalsIgnoreCase(UPLOAD)) {
			view = DEFAULT_VIEW;
		}
		switchView(view, view.equalsIgnoreCase(MAP)
				|| view.equalsIgnoreCase(LIST) || view.equalsIgnoreCase(DETAIL));
		historyButtonClicked = false;
		parent.historyButtonClicked = false;
	}

	@Override
	public String historyToken() {
		if (activeViewInfo == null) {
			return History.getToken();
		}
		StringBuilder token = new StringBuilder(
				constructHistoryUrl(UrlParam.VIEW));
		String activeViewToken = activeViewInfo.getView().historyToken();
		if (!activeViewToken.equals("")) {
			boolean startWithAnd = activeViewToken.startsWith("&");
			token.append((startWithAnd ? "" : "&") + activeViewToken);
		}
		String currentView = activeViewInfo.getName();
		if (currentView.equals(LIST) || currentView.equals(MAP)
				|| currentView.equals(DETAIL)) {
			token.append("&" + constructHistoryUrl(UrlParam.PAGE) + "&");
			token.append(searchForm.getCurrentStateToken());
		}
		return token.toString();
	}

	private String constructHistoryUrl(UrlParam urlParam) {
		String param = urlParam.lower() + "=";
		switch (urlParam) {
		case VIEW:
			if (activeViewInfo == null) {
				return "";
			}
			return param += activeViewInfo.getName();
		case PAGE:
			return param + currentPageNum;
		case QUERY:
			String searchText = searchForm.getSearchQuery();
			if (searchText.equals("")) {
				return "";
			}
			return param + searchText;
		case TYPE:
			Set<String> baseFilters = query.getBaseFilters();
			String type = queryFiltersMap.getType(baseFilters);
			return param + type;
		case ERROR_TYPE:
			return param + searchForm.getInvalidatedType();
		case ST:
			return param + searchForm.getSharedType();
		case ASEARCH:
			ViewInfo advanceViewInfo = viewInfos.get(ADVANCE.toLowerCase());
			if (advanceViewInfo.isViewConstrcuted()) {
				return advanceViewInfo.getView().historyToken();
			}
			// Set<String> searchFilters = query.getSearchFilters();
			StringBuilder tokenBuilder = new StringBuilder();
			for (String filter : query.getSearchFilters()) {
				if (filter.startsWith(SearchForm.QUICK_SEARCH)
						|| filter.startsWith(SearchForm.VALIDATION_ERROR)) {
					continue;
				}
				tokenBuilder.append(param + filter + "&");
			}
			if (tokenBuilder.length() != 0) {
				tokenBuilder.deleteCharAt(tokenBuilder.length() - 1);
			}
			return tokenBuilder.toString();
		case RF:
			if (!searchForm.isResultFilterVisible()) {
				return "";
			}
			return param
					+ searchForm.getResultFilter().toString().toLowerCase();
			
			
		case SOUTHWEST:
			String SWText = searchForm.getSOUTHWEST();
			if (SWText.equals("")) {
				return "";
			}
			return param + SWText;
		case NORTHEAST:
			String NEText = searchForm.getNORTHEAST();
			if (NEText.equals("")) {
				return "";
			}
			return param + NEText;
			
		}
		return "";
	}

	private final Map<String, ViewInfo> viewInfos = new HashMap<String, ViewInfo>();

	private int getPageNum(String historyToken) {
		historyState.setHistoryToken(historyToken);
		int pageNum = (Integer) historyState
				.getHistoryParameters(UrlParam.PAGE);
		if (pageNum == HistoryState.UNDEFINED) {
			pageNum = 1;
		}
		return pageNum;
	}

	private String getMapBounds() {
		LatLng NorthEast = (LatLng) historyState.getHistoryParameters(UrlParam.NORTHEAST);
		LatLng SouthWest = (LatLng) historyState.getHistoryParameters(UrlParam.SOUTHWEST);
		if (NorthEast != null && SouthWest != null) {
			String ret = ""; 
			ret += "LatitudeSW = " + SouthWest.getLatitude() + "\t"; // 緯度
			ret += "LatitudeNE = " + NorthEast.getLatitude() + "\t";// 緯度
			ret += "LongitudeSW = " + SouthWest.getLongitude() + "\t"; // 經度
			ret += "LongitudeNE = " + NorthEast.getLongitude(); // 經度
			return ret;
		}
		else
			return null;
	}
	
	
	// session相關,state change
	/**
	 * Removed uploadLink if (@link ViewState} the ViewState is
	 * {@link ViewState#UNAUTHENTICATED} add back if it is
	 * {@link ViewState#AUTHENTICATED}. When state is UNAUTHENTICATED and the
	 * current view is {@link UploadView} switch to default view.
	 */

	@Override
	public void onStateChanged(ViewState state) {
		if (!isMyView(parent.historyToken())) {
			return;
		}
		queryFiltersMap.init(ApplicationView.getAuthenticatedUser());
		searchForm.onStateChanged(state);
		switch (state) {
		case ADMIN:
		case REVIEWER:
		case RESEARCHER:
			uploadLink.setHTML(constants.Upload());
			break;
		case UNAUTHENTICATED:
			uploadLink.setHTML("");
			if (activeViewInfo != null
					&& activeViewInfo.getName().equals(UPLOAD)) {
				switchView(DEFAULT_VIEW, false);
				addHistoryItem(false);
			}
			break;
		}
	}

	@Override
	protected void resetToDefaultState() {
		ViewInfo mapViewInfo = viewInfos.get(MAP.toLowerCase());
		if (mapViewInfo.isViewConstrcuted()) {
			mapViewInfo.getView().resetToDefaultState();
		}
		ViewInfo listViewInfo = viewInfos.get(LIST.toLowerCase());
		if (listViewInfo.isViewConstrcuted()) {
			listViewInfo.getView().resetToDefaultState();
		}
	}

	// 為了click使用
	public void onClick(ClickEvent event) {
		Object sender = event.getSource();
		if (sender == mapLink) {
			switchView(MAP, false);
			addHistoryItem(false);
			searchForm.restoreStatesFromHistory(History.getToken());
		} else if (sender == listLink) {
			switchView(LIST, false);
			addHistoryItem(false);
			searchForm.restoreStatesFromHistory(History.getToken());
		} else if (sender == uploadLink) {
			switchView(UPLOAD, false);
			activeViewInfo.getView().resetToDefaultState();
			addHistoryItem(false);
		} else if (sender == searchForm.advanceLink) {
			switchView(ADVANCE, false);
			addHistoryItem(false);
		}
	}

	// 畫面操作

	private final HTML mapLink = new HTML(constants.Map());
	private final HTML listLink = new HTML(constants.List());
	private final HTML uploadLink = new HTML(constants.Upload());

	private final HorizontalPanel toolHp = new HorizontalPanel();
	private final SimplePanel mainSp = new SimplePanel();
	private ViewInfo activeViewInfo;

	private final HorizontalPanel switchViewPanel = new HorizontalPanel();
	private String lastView;

	@Override
	protected void switchView(String view, boolean isLoadRecord) {
		if (true) {
			switchView2(view, isLoadRecord);
			return;
		}
	}

	/**
	 * Temporally solution for earth map type bug switching view bug.
	 * 
	 * @param view
	 *            a view name to be switch to
	 * @param isLoadRecord
	 *            true if record will be loaded.
	 */
	protected void switchView2(String view, final boolean isLoadRecord) {
		if (view.equals(ADVANCE)) { // If we are going into Advanced View turn
			// off
			// the old search
			searchForm.mainHp.remove(searchForm.advanceLink);
			searchForm.mainHp.remove(searchForm.forLabel);
			searchForm.mainHp.remove(searchForm.searchBox);
			searchForm.mainHp.remove(searchForm.searchButton);

			if (activeViewInfo == null) {
				lastView = MAP;
			} else {
				lastView = activeViewInfo.getName();
			}
		} else if (activeViewInfo != null
				&& activeViewInfo.getName().equalsIgnoreCase(ADVANCE)) { // if
			// we
			/**
			 * are leaving Advanced View turn on the Simple Search
			 */
			searchForm.mainHp.add(searchForm.forLabel);
			searchForm.mainHp.add(searchForm.searchBox);
			searchForm.mainHp.add(searchForm.searchButton);
			searchForm.mainHp.add(searchForm.advanceLink);
		}
		view = view.toLowerCase();
		ViewInfo switchViewInfo = viewInfos.get(view);
		
		if (switchViewInfo != null && switchViewInfo == activeViewInfo ) {
			//如果和現在相同,例如地圖模式下面,只是拖曳,縮放地圖
			//kayjean 20110309
			DeferredCommand.addCommand(new Command() {
				public void execute() {
					if (isLoadRecord) {
						searchForm.restoreStatesFromHistory(History.getToken());
					}
				}
			});
			return;
			//searchForm.restoreStatesFromHistory(History.getToken());
		}
		
		if (switchViewInfo == null) {
			switchViewInfo = viewInfos.get(DEFAULT_VIEW);
		}
		
		if (activeViewInfo != null) {
			//20110309 想要移除popup getPopupView(activeViewInfo.getName()).hide();
			mainVp.remove(activeViewInfo.getView());
		}
		switchViewPanel.clear();
		activeViewInfo = switchViewInfo;
			
		//kayjean 20110309
/*			
		DeferredCommand.addCommand(new Command() {
			public void execute() {
				CustomPopupPanel view = getPopupView(activeViewInfo.getName());
				if (isLoadRecord) {
					searchForm.restoreStatesFromHistory(History.getToken());
				}
				view.show();
			}
		});
*/			
			
		View switchView = switchViewInfo.getView();
		mainVp.add(switchView);
		switchView.onShow();
			
		DeferredCommand.addCommand(new Command() {
			public void execute() {
//				CustomPopupPanel view = getPopupView(activeViewInfo.getName());
				if (isLoadRecord) {
					searchForm.restoreStatesFromHistory(History.getToken());
				}
//				view.show();
			}
		});
			
		// boolean isShowedUpload = ApplicationView.getCurrentState() ==
		// ViewState.AUTHENTICATED;
		// if (isShowedUpload) {
		switchViewPanel.add(uploadLink);
		// }
		if (view.equals(DETAIL.toLowerCase())) {
			switchViewPanel.add(mapLink);
			switchViewPanel.add(listLink);
		} else if (view.equals(LIST.toLowerCase())) {
			switchViewPanel.add(mapLink);
		} else if (view.equals(MAP.toLowerCase())) {
			switchViewPanel.add(listLink);
		} else {
			switchViewPanel.add(mapLink);
			switchViewPanel.add(listLink);
			switchViewPanel.add(uploadLink);
		}
		activeViewInfo = switchViewInfo;
		// kayjean initAdvanceFields();
		// 這個項目很多,先關閉
		// switchView.onShow();
	}

	@Override
	public void onShow() {
		super.onShow();
//kayjean 20110309
		setVisible(true);
//		activeViewInfo.getView().setVisible(true);
	}

//kayjean 20110309
/*	
	@Override
	public void setVisible(boolean visible) {
		if (visible) {
			getPopupView(activeViewInfo.getName()).show();
		} else {
			getPopupView(activeViewInfo.getName()).hide();
		}
	};
*/
	
	
	// search....
	/**
	 * The SearchForm inner class defines a widget used for searching
	 * occurrences by type and species name.
	 * 
	 * The form submits queries by reseting the pager and query filters and then
	 * adding a new history token via getHistoryToken() to the {@link History}
	 * stack. The history token contains the form's search parameters which get
	 * processed by onHistoryChanged().
	 */
	private class SearchForm extends Composite implements
			ViewStateChangeListener, ChangeHandler {

		// 畫面生成
		private final HTML advanceLink = new HTML(constants.AdvanceSearch());
		private final ListBox resultFilterLb = new ListBox();
		private final ListBox invalidatedLb = new ListBox();
		/**
		 * A mapping of occurrence types to their list box index.
		 */
		final Map<String, Integer> typeIndexMap = new HashMap<String, Integer>();
		private final Button searchButton;

		private final TextBox SOUTHWEST;
		private final TextBox NORTHEAST;
		
		/**
		 * Constructs a new SearchForm.
		 */
		private SearchForm() {
			final Label searchLabel = new Label(" " + constants.Search() + " ");
			searchLabel.setStyleName("searchLabel");
			advanceLink.setStyleName("link");
			advanceLink.addStyleName("AdvanceLink");
			resultFilterLb.addItem(constants.Both(), "both");
			resultFilterLb.addItem(constants.Public(), "public");
			resultFilterLb.addItem(constants.Private(), "private");
			resultFilterLb.setStyleName("ResultFilter");
			invalidatedLb.addItem(constants.AllValidationError(), ALL_ERROR);
			typeIndexMap.put(ALL_ERROR.toLowerCase(), 0);
			invalidatedLb.addItem(constants.InvalidYearCollected(), YEAR_ERROR);
			typeIndexMap.put(YEAR_ERROR.toLowerCase(), 1);
			invalidatedLb.addItem(constants.InvalidGenus(), GENUS_ERROR);
			typeIndexMap.put(GENUS_ERROR.toLowerCase(), 2);
			invalidatedLb.addItem(constants.InvalidSpecificEpthet(),

			SPECIFIC_EPTHET_ERROR);
			typeIndexMap.put(SPECIFIC_EPTHET_ERROR.toLowerCase(), 3);

			invalidatedLb.addItem(constants.InvalidDecimalLatitude(),

			DECIMAL_LAT_ERROR);
			typeIndexMap.put(DECIMAL_LAT_ERROR.toLowerCase(), 4);

			invalidatedLb.addItem(constants.InvalidDecimalLongitude(),

			DECIMAL_LNG_ERROR);
			typeIndexMap.put(DECIMAL_LNG_ERROR.toLowerCase(), 5);

			invalidatedLb.addItem(constants.InvalidTaxonomicClassification(),

			TAXO_ERROR);
			typeIndexMap.put(TAXO_ERROR.toLowerCase(), 6);

			invalidatedLb.setStyleName("ResultFilter");
			searchTypeBox.setStyleName("TypeBox");
			sharedListBox.setStyleName("SharedListBox");
			forLabel.setStylePrimaryName("forLabel");
			forLabel.setWordWrap(false);

			// Clicking the button resets the pager, clears the query filters,
			// then
			// adds a new history item which fires
			// OccurrenceView.onHistoryChanged(),
			// which decodes the new history token and updates the
			// DataPagerWidget.
			searchButton = new Button(constants.Search());
			searchButton.addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					search();
				}
			});

			// The search box clicks the search button if the enter key is
			// pressed:
			searchBox = new TextBox();
			searchBox.addKeyUpHandler(new KeyUpHandler() {
				public void onKeyUp(KeyUpEvent event) {
					if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
						searchButton.click();
					}

				}
			});
			searchTypeBox.addChangeHandler(this);

			mainHp = new FlowPanel();
			mainHp.add(searchLabel);
			mainHp.add(searchTypeBox);
			mainHp.add(forLabel);
			mainHp.add(searchBox);
			mainHp.add(searchButton);
			mainHp.add(advanceLink);

			SOUTHWEST = new TextBox();
			NORTHEAST = new TextBox();
			mainHp.add( SOUTHWEST );
			mainHp.add( NORTHEAST );
			SOUTHWEST.setVisible(false);
			NORTHEAST.setVisible(false);
			
			initWidget(mainHp);
			mainHp.setStyleName("Search-Form");
			advanceLink.addClickHandler(OccurrenceView.this);
			onStateChanged(ApplicationView.getCurrentState());
			resultFilterLb.addChangeHandler(this);
		}

		public String getCurrentStateToken() {
			StringBuilder sb = new StringBuilder();
			String searchQuery = constructHistoryUrl(UrlParam.QUERY);
			if (!searchQuery.equals("")) {
				sb.append(searchQuery + "&");
			}
			String myRecordsValue = constructHistoryUrl(UrlParam.RF);
			if (!myRecordsValue.equals("")) {
				sb.append(myRecordsValue + "&");
			}
/*kayjean			
			String advanceSearch = constructHistoryUrl(UrlParam.ASEARCH);
			if (!advanceSearch.equals("")) {
				sb.append(advanceSearch + "&");
			}
*/			
			if (searchForm.invalidatedLb.isAttached()) {
				sb.append(constructHistoryUrl(UrlParam.ERROR_TYPE) + "&");
			}
			if (searchForm.sharedListBox.isAttached()) {
				sb.append(constructHistoryUrl(UrlParam.ST) + "&");
			}
			

			if (!searchForm.SOUTHWEST.getText().equalsIgnoreCase("")) {
				sb.append(constructHistoryUrl(UrlParam.SOUTHWEST) + "&");
			}
			if (!searchForm.NORTHEAST.getText().equalsIgnoreCase("")) {
				sb.append(constructHistoryUrl(UrlParam.NORTHEAST) + "&");
			}
			
			sb.append(constructHistoryUrl(UrlParam.TYPE));
			return sb.toString();
		}
		
		/**
		 * @return the search box text.
		 */
		String getSearchQuery() {
			return searchBox.getText();
		}

		String getSOUTHWEST() {
			return SOUTHWEST.getText();
		}
		String getNORTHEAST() {
			return NORTHEAST.getText();
		}
		
		void setSOUTHWEST( String sOUTHWEST) {
			SOUTHWEST.setText(sOUTHWEST);
		}
		void setNORTHEAST( String nORTHEAST) {
			NORTHEAST.setText(nORTHEAST);
		}
		
		String getInvalidatedType() {
			return invalidatedLb.getValue(invalidatedLb.getSelectedIndex());
		}

		public boolean isResultFilterVisible() {
			return resultFilterLb.isAttached();
		}

		
		
		public void search() {

			//basefilters,resultfilter
			String searchType = getSearchType();
			ResultFilter resultFilter = getResultFilter();
			query.setBaseFilters(query.getFiltersFromProperty(searchType,
					ApplicationView.getAuthenticatedUser(), resultFilter));

			String activeView = activeViewInfo.getName();
			if (!activeView.equalsIgnoreCase(ADVANCE)) {
				clearAdanceSearch();
			}
			//kayjean
			//ViewInfo advanceViewInfo = viewInfos.get(ADVANCE.toLowerCase());
			//if(advanceViewInfo.isViewConstrcuted())
			//{
			//	AdvanceSearchView searchView = (AdvanceSearchView) advanceViewInfo .getView();
			//	query.setSearchFilters(searchView.getSearchFilters());
			//}
			
			if (!activeView.equals(MAP) && !activeView.equals(LIST)) {
				//如果是DETAIL畫面,開始搜尋,畫面就會轉變成以MAP為主
				switchView(DEFAULT_VIEW, false);
			}

			String searchText = searchBox.getText();
			if (!searchText.equals("")) {
				//searchFilters
				query.addSearchFilter(QUICK_SEARCH + " = " + searchText);
			}
			else{
				//if (activeView.equals(MAP)) {
				//如果是地圖模式,又沒有輸入查詢文字,應該就是要搜尋目前圖面上可以顯示點
				//加入這個限制不錯,可是在LISTVIEW時候就沒有傳入經緯度資料,沒有同樣資料
					addMapBounds();
				//}
			}
			
			//searchFilters
			//addSearchFilter(VALIDATION_ERROR + " like " + invalidatedLb.getValue(sIndex));
			addErrorQuery();
			
			//searchFilters
			//addSearchFilter("sharedUsersCSV like " + user.getEmail());
			addSharedSearchToQuery();

			// kayjean 無法編譯,因為是Set<String> GWT.log(query.getBaseFilters() + "");
			// for (String searchFilter : query.getSearchFilters()) {
			// System.err.println(searchFilter);
			// }
			resetToDefaultState();
			addHistoryItem(false);
			query.requestData(1);
		}
		

		/**
		 * @return the selected occurrence type in the list box
		 */
		public String getSearchType() {
			int index = searchTypeBox.getSelectedIndex();
			return searchTypeBox.getItemText(index);
		}
		
		public ResultFilter getResultFilter() {
			ResultFilter resultFilter;
			String filterValue = resultFilterLb.getValue(resultFilterLb
					.getSelectedIndex());
			if (filterValue.equals("private")) {
				resultFilter = ResultFilter.PRIVATE;
			} else if (filterValue.equals("public")) {
				resultFilter = ResultFilter.PUBLIC;
			} else if (filterValue.equals("both")) {
				resultFilter = ResultFilter.BOTH;
			} else {
				resultFilter = ResultFilter.PUBLIC;
			}
			return resultFilter;
		}

		protected void clearAdanceSearch() {
			//kayjean
			//ViewInfo advanceViewInfo = viewInfos.get(ADVANCE.toLowerCase());
			//if(advanceViewInfo.isViewConstrcuted())
			//{
			//	AdvanceSearchView searchView = (AdvanceSearchView) advanceViewInfo.getView();
			//	searchView.clearSearch();
			//}
			//else
			//{
			//	query.setSearchFilters(new HashSet<String>()); // addHistoryItem(false); 
			//}

			query.setSearchFilters(new HashSet<String>());
		}

		private void addErrorQuery() {
			int searchIndex = searchTypeBox.getSelectedIndex();
			String type = searchTypeBox.getItemText(searchIndex);
			boolean isInvalid = type.equalsIgnoreCase(constants.AllInvalid())
					|| type.equalsIgnoreCase(constants.MyInvalid());
			if (isInvalid) {
				int sIndex = invalidatedLb.getSelectedIndex();
				if (sIndex > 0) {
					query.addSearchFilter(VALIDATION_ERROR + " like "
							+ invalidatedLb.getValue(sIndex));
				}
			}
		}
		
		private void addMapBounds(){
			String ret = getMapBounds();			
			if( ret != null ){
				String[] rets = ret.split("\t");
				for( int i = 0 ; i < rets.length ; i++ ){
					query.addSearchFilter( rets[i] );
				}
			}
		}

		private void addSharedSearchToQuery() {
			if (sharedListBox.isAttached()) {
				String sharedType = getSharedType();
				User user = ApplicationView.getAuthenticatedUser();
				if (user == null) {
					return;
				}
				if (sharedType.equalsIgnoreCase(SHARED_WITH_ME)) {
					query.addSearchFilter("sharedUsersCSV like "
							+ user.getEmail());
				} else if (sharedType.equalsIgnoreCase(SHARED_BY_ME)) {
					query.addSearchFilter("sharedUsersCSV !empty ");
				} else if (sharedType.equalsIgnoreCase(UNSHARED_BY_ME)) {
					query.addSearchFilter("sharedUsersCSV empty ");
				}
			}
		}

		String getSharedType() {
			int selectedIndex = sharedListBox.getSelectedIndex();
			if (selectedIndex < 0) {
				return "";
			}
			return sharedListBox.getValue(selectedIndex);
		}

		
		
		// 處理由implements延伸出來的基本項目,這部份是onchange
		// for ChangeHandler
		public void onChange(ChangeEvent event) {
			Object source = event.getSource();
			if (source == searchTypeBox) {
				int selectedIndex = searchTypeBox.getSelectedIndex();
				setMyRecordsEnable(selectedIndex > ALL_TYPES_END_INDEX);
				String type = searchTypeBox.getItemText(selectedIndex);
				setMyRecordsInvalid(type.equalsIgnoreCase(constants
						.AllInvalid())
						|| type.equalsIgnoreCase(constants.MyInvalid()));
				// setMyRecordsInvalid(selectedIndex == INVALIDATED_INDEX
				// || selectedIndex == MY_INVALIDATED_INDEX);
				setSharedType(getSearchType());
			} else if (source == resultFilterLb) {
				int selectedIndex = searchTypeBox.getSelectedIndex();
				setSharedType(getSearchType());
			}
			resize(Window.getClientWidth(), Window.getClientHeight());
		}

		/**
		 * The list of occurrence types.
		 */
		final ListBox searchTypeBox = new ListBox();
		// TODO: The use of static index constants here is really bad design.
		// static final int INVALIDATED_INDEX = 3;
		// static final int MY_INVALIDATED_INDEX = 8;
		protected static final int ALL_TYPES_END_INDEX = 4;
		/**
		 * The main widget wrapped by this composite.
		 */
		final FlowPanel mainHp;
		private final ListBox sharedListBox = new ListBox();

		private final Label forLabel = new Label(" " + constants.For() + " ");

		/**
		 * Adds {@link #privateCb} if enabled is true and remove it if enabled
		 * is false.
		 * 
		 * @param enabled
		 *            true to show my records only check box and false to
		 *            hide/remove my records only check box
		 */
		void setMyRecordsEnable(boolean enabled) {
			if (enabled) {
				mainHp.insert(resultFilterLb, 2);
			} else {
				mainHp.remove(resultFilterLb);
				// publicRb.setValue(true);
			}
			if (activeViewInfo != null) {
				DeferredCommand.addCommand(new Command() {
					public void execute() {
						resize(Window.getClientWidth(), Window
								.getClientHeight());
					}
				});
			}
		}

		void setMyRecordsInvalid(boolean invalidated) {
			if (invalidated) {
				int beforeIndex = mainHp.getWidgetIndex(resultFilterLb) == -1 ? 2
						: 3;
				mainHp.insert(invalidatedLb, beforeIndex);
			} else {
				mainHp.remove(invalidatedLb);
			}
			// invalidatedLb.setSelectedIndex(0);
		}

		protected static final String QUICK_SEARCH = "QuickSearch";
		protected static final String VALIDATION_ERROR = "ValidationError";
		protected static final String ALL_SHARED_UNSHARED = "all";
		protected static final String SHARED_WITH_ME = "swm";
		protected static final String SHARED_BY_ME = "sbm";
		protected static final String UNSHARED_BY_ME = "uswm";
		protected static final String ALL_ERROR = "all";
		protected static final String YEAR_ERROR = "YearCollected";
		protected static final String GENUS_ERROR = "GENUS";
		protected static final String SPECIFIC_EPTHET_ERROR = "SpecificEpithet";
		protected static final String DECIMAL_LAT_ERROR = "DecimalLatitude";
		protected static final String DECIMAL_LNG_ERROR = "DecimalLongitude";
		protected static final String TAXO_ERROR = "Taxonomic classification";

		void setSharedType(String searchType) {
			ViewState viewState = ApplicationView.getCurrentState();
			sharedListBox.clear();
			switch (viewState) {
			case UNAUTHENTICATED:
				mainHp.remove(sharedListBox);
				break;
			case ADMIN:
			case REVIEWER:
			case RESEARCHER:
				if (isAllSelected(searchType)) {
					if (!sharedListBox.isAttached()) {
						mainHp.insert(sharedListBox, 2);
					}
					sharedListBox.addItem(constants.AllSharedUnshared(),
							ALL_SHARED_UNSHARED);
					typeIndexMap.put(ALL_SHARED_UNSHARED.toLowerCase(), 0);
					sharedListBox.addItem(constants.SharedWithMe(),
							SHARED_WITH_ME);
					typeIndexMap.put(SHARED_WITH_ME.toLowerCase(), 1);
				} else {
					String resultFilter = resultFilterLb
							.getValue(resultFilterLb.getSelectedIndex());
					if (resultFilter.equalsIgnoreCase("public")) {
						mainHp.remove(sharedListBox);
					} else {
						if (!sharedListBox.isAttached()) {
							mainHp.insert(sharedListBox, 2);
						}
						sharedListBox.addItem(constants.AllSharedUnshared(),
								ALL_SHARED_UNSHARED);
						typeIndexMap.put(ALL_SHARED_UNSHARED.toLowerCase(), 0);
						sharedListBox.addItem(constants.SharedByMe(),
								SHARED_BY_ME);
						typeIndexMap.put(SHARED_BY_ME.toLowerCase(), 1);
						sharedListBox.addItem(constants.UnsharedByMe(),
								UNSHARED_BY_ME);
						typeIndexMap.put(UNSHARED_BY_ME.toLowerCase(), 2);
					}
				}
				break;
			}
		}

		boolean isAllSelected(String searchType) {
			return searchType.equalsIgnoreCase(constants.AllInvalid())
					|| searchType.equalsIgnoreCase(constants.AllOccurrences())
					|| searchType.equalsIgnoreCase(constants
							.AllPositivelyReviewed())
					|| searchType.equalsIgnoreCase(constants
							.AllNegativelyReviewed())
					|| searchType.equalsIgnoreCase(constants
							.AllAwaitingReview());
		}

		// 先處理由implements延伸出來的基本項目,這部份是ViewStateChangeListener

		/**
		 * Handles changes in application state.
		 * 
		 * If state is AUTHENTICATED, the list box displays the 'My' occurrence
		 * types used for searching occurrences that belong to the authenticated
		 * user along with the 'All' occurrence types used for searching public
		 * occurrences. Otherwise only the 'All' occurrence types are displayed.
		 * 
		 * Also updates the the occurrence list box selection and search box
		 * text from the current history token.
		 */
		public void onStateChanged(ViewState state) {
			searchTypeBox.clear();
			typeIndexMap.clear();
			searchBox.setText("");

			// All Positively Reviewed
			// All Negatively Reviewed
			// All Awaiting Review
			// All Invalidated
			// All Occurrences

			// All Positively Reviewed
			searchTypeBox.addItem(constants.AllPositivelyReviewed(),
					ALL_POS_REVIEWED);
			typeIndexMap
					.put(ALL_POS_REVIEWED, searchTypeBox.getItemCount() - 1);

			// All Negatively Reviewed
			searchTypeBox.addItem(constants.AllNegativelyReviewed(),
					ALL_NEG_REVIEWED);
			typeIndexMap
					.put(ALL_NEG_REVIEWED, searchTypeBox.getItemCount() - 1);

			// All Awaiting Review
			searchTypeBox.addItem(constants.AllAwaitingReview(),
					ALL_AWAIT_REVIEW);
			typeIndexMap
					.put(ALL_AWAIT_REVIEW, searchTypeBox.getItemCount() - 1);

			// All Invalidated
			searchTypeBox.addItem(constants.AllInvalid(), ALL_INVALID);
			typeIndexMap.put(ALL_INVALID, searchTypeBox.getItemCount() - 1);

			// All Occurrences
			searchTypeBox.addItem(constants.AllOccurrences(), ALL_OCC);
			typeIndexMap.put(ALL_OCC, searchTypeBox.getItemCount() - 1);

			switch (state) {
			case UNAUTHENTICATED:
				mainHp.remove(sharedListBox);
				break;

			// My Positively Reviewed
			// My Negatively Reviewed
			// My Awaiting Review
			// My Invalidated
			// My Occurrences
			// Occurrences to Review
			case ADMIN:
			case REVIEWER:
				// // Occurrences to Review
				searchTypeBox.addItem(constants.OccurrencesToReview(),
						OCCURRENCES_TO_REVIEW);
				typeIndexMap.put(OCCURRENCES_TO_REVIEW, searchTypeBox
						.getItemCount() - 1);
				// My Positively Reviewed
				searchTypeBox.addItem(constants.MyPositivelyReviewed(),
						MY_POS_REVIEWED);
				typeIndexMap.put(MY_POS_REVIEWED,
						searchTypeBox.getItemCount() - 1);

				// My Negatively Reviewed
				searchTypeBox.addItem(constants.MyNegativelyReviewed(),
						MY_NEG_REVIEWED);
				typeIndexMap.put(MY_NEG_REVIEWED,
						searchTypeBox.getItemCount() - 1);
			case RESEARCHER:
				// My overall Positively Reviewed
				searchTypeBox.addItem(constants.MyOverallPositivelyReviewed(),
						MY_OVERALL_POS_REVIEW);
				typeIndexMap.put(MY_OVERALL_POS_REVIEW, searchTypeBox
						.getItemCount() - 1);

				// My overall Negatively Reviewed
				searchTypeBox.addItem(constants.MyOverallNegativelyReviewed(),
						MY_OVERALL_NEG_REVIEW);
				typeIndexMap.put(MY_OVERALL_NEG_REVIEW, searchTypeBox
						.getItemCount() - 1);
				// My Awaiting Review
				searchTypeBox.addItem(constants.MyAwaitingReview(),
						MY_AWAITING_REVIEW);
				typeIndexMap.put(MY_AWAITING_REVIEW, searchTypeBox
						.getItemCount() - 1);
				// My Invalidated
				searchTypeBox.addItem(constants.MyInvalid(), MY_INVALID);
				typeIndexMap.put(MY_INVALID, searchTypeBox.getItemCount() - 1);

				// My Occurrences
				searchTypeBox
						.addItem(constants.MyOccurrences(), MY_OCCURRENCES);
				typeIndexMap.put(MY_OCCURRENCES,
						searchTypeBox.getItemCount() - 1);

				mainHp.insert(sharedListBox, 2);

				break;
			}
			restoreStatesFromHistory(History.getToken());
		}

		// 基本運作

		/**
		 * The text box for entering the species name.
		 */
		final TextBox searchBox;

		/**
		 * The index for the default selected occurrence type in the list box.
		 */
		static final int DEFAULT_SELECTION_INDEX = 0;

		//這裡會把historytoken的值拿出來,並且將搜尋設定,例如listbox,等等,回歸到原本數值
		//並且重新組裝須要項目,發出查詢
		//另外,單純的按下搜尋按鈕是啟動另外部分,但效果應該是類似的
		public void restoreStatesFromHistory(String historyToken) {
			historyState.setHistoryToken(historyToken);
			String currentView = activeViewInfo == null ? DEFAULT_VIEW
					: activeViewInfo.getName();
			if (currentView.equals(UPLOAD) || currentView.equals(ADVANCE)) {
				return;
			}
			String searchType = getListBoxText(searchTypeBox, UrlParam.TYPE,
					DEFAULT_SELECTION_INDEX);

			int searchTypeIndex = getTypeIndex(searchType,
					DEFAULT_SELECTION_INDEX);
			setMyRecordsEnable(searchTypeIndex > ALL_TYPES_END_INDEX);
			ResultFilter resultFilter = (ResultFilter) historyState
					.getHistoryParameters(UrlParam.RF);

			switch (resultFilter) {
			case PUBLIC:
				resultFilterLb.setSelectedIndex(1);
				break;
			case PRIVATE:
				resultFilterLb.setSelectedIndex(2);
				break;
			case BOTH:
				resultFilterLb.setSelectedIndex(0);
			}

			searchTypeBox.setSelectedIndex(searchTypeIndex);
			String type = searchTypeBox.getItemText(searchTypeIndex);
			boolean isInvalid = type.equalsIgnoreCase(constants.AllInvalid())
					|| type.equalsIgnoreCase(constants.MyInvalidated());
			setMyRecordsInvalid(isInvalid);
			// restoring invalidated box;
			if (isInvalid) {
				String errorType = getListBoxText(invalidatedLb,
						UrlParam.ERROR_TYPE, 0);
				invalidatedLb.setSelectedIndex(getTypeIndex(errorType, 0));

			}
			String searchText = getSearchQuery(historyToken);
			searchBox.setText(searchText);
			
			int pageNum = getPageNum(historyToken);
			
			query.setBaseFilters(query.getFiltersFromProperty(searchTypeBox
					.getItemText(searchTypeIndex), ApplicationView
					.getAuthenticatedUser(), resultFilter));
			Set<String> searchFilters = query.getSearchFilters();
			searchFilters.clear();
			if (!searchText.equals("")) {
				searchFilters.add(QUICK_SEARCH + " = " + searchText);
			}
			else{
				//if (currentView.equals(MAP)) {
				//如果是地圖模式,又沒有輸入查詢文字,應該就是要搜尋目前圖面上可以顯示點
				//加入這個限制不錯,可是在LISTVIEW時候就沒有傳入經緯度資料,沒有同樣資料
					addMapBounds();
				//}
			}
			
			setSharedType(getSearchType());
			if (sharedListBox.isAttached()) {
				String sharedType = getListBoxText(sharedListBox, UrlParam.ST,
						0);
				sharedListBox.setSelectedIndex(getTypeIndex(sharedType, 0));
			}
			List<String> searchFieldValues = (List<String>) historyState
					.getHistoryParameters(UrlParam.ASEARCH);
			if (searchFieldValues != null) {
				for (String searchFieldValue : searchFieldValues) {
					searchFilters.add(searchFieldValue);
				}
			}
			addErrorQuery();
			addSharedSearchToQuery();
			
			// 這裡是最重要的地方,因為這裡,才能真正驅動系統運作
			//向前看時,也才會發現searchForm.restoreStatesFromHistory(History.getToken());
			// 因為這個會讓畫面一開始就有內容
			
			query.requestData(pageNum);
			currentPageNum = pageNum;
		}

		private String getListBoxText(ListBox listBox, UrlParam urlParam,
				int defaultSelectedIndex) {
			String type = historyState.getHistoryParameters(urlParam) + "";
			if (type.equals("") && listBox.getItemCount() > 0) {
				type = listBox.getValue(defaultSelectedIndex);
			}
			return type;
		}

		int getTypeIndex(String text, int defaultIndex) {
			int index = defaultIndex;
			if (typeIndexMap.containsKey(text)) {
				index = typeIndexMap.get(text.toLowerCase());
			}
			return index;
		}

		/**
		 * Extracts the {@link UrlParam} QUERY parameter from a history token
		 * and returns it as a String. If the QUERY parameter does not exist in
		 * the history token, and empty string is returned.
		 * 
		 * @param historyToken
		 *            the history token
		 * @return the search query
		 */
		public String getSearchQuery(String historyToken) {
			historyState.setHistoryToken(historyToken);
			String searchTerm = historyState
					.getHistoryParameters(UrlParam.QUERY)
					+ "";
			return searchTerm;
		}
	}

	private final SearchForm searchForm;

	// query是啟動一切查詢的基礎
	// 在這邊生成,然後在下面範例中使用
	// private void ListView() {
	// ViewInfo listView = ListViewView.init(this, this, query);
	// ...
	// query被當成參數丟入ListViewView
	// 而constructView時,使用new ListView(parent, query, pageListener,
	// occurrentListener);傳入construct中
	// 這個架構中,使用了 query.addDataRequestListener(this);
	// 所謂的this, 因為這個class implements DataRequestListener
	// 所以會繼續把內部的 requestData 加入系統中
	// 未來查詢使用requestData時,會trigger
	// public void requestData(int pageNum) {
	// for (DataRequestListener listener : dataRequestListeners) {
	// listener.requestData(pageNum);
	// }
	// }
	// 繼續執行
	// if (isMyView(token)) {
	// pagerWidget.init(pageNum);
	// 從這裡啟動query

	private final OccurrenceQuery query;

	// 我們從query開始來完成
	// 從applocationview裡面有個init
	public static ViewInfo init(final View parent, final String name,
			final String historyName) {
		return new ViewInfo() {

			@Override
			protected View constructView() {
				return new OccurrenceView(parent);
			}

			@Override
			protected String getHisTokenName() {
				return historyName;
			}

			@Override
			protected String getName() {
				return name;
			}

		};
	}

	// 有init之後就有construct
	private OccurrenceView() {
		this(null);
	}

	private final VerticalPanel mainVp;

	/**
	 * Note: This constructor sets the query's base filters.
	 * 
	 * @param parent
	 */
	private OccurrenceView(View parent) {
		super(parent, true);

		// Sets the default query to all positively reviewed.
		query = new OccurrenceQuery();
		query.setBaseFilters(query.getFiltersFromProperty(constants
				.AllPositivelyReviewed(), null));

		queryFiltersMap = new QueryFiltersMap();
		searchForm = new SearchForm();
		queryFiltersMap.init(ApplicationView.getAuthenticatedUser());
		mainVp = new VerticalPanel();
		initViews();

		toolHp.add(searchForm);
		toolHp.setWidth("100%");
		toolHp.add(switchViewPanel);
		toolHp.setCellVerticalAlignment(switchViewPanel,
				HasVerticalAlignment.ALIGN_MIDDLE);
		toolHp.setCellVerticalAlignment(searchForm,
				HasVerticalAlignment.ALIGN_MIDDLE);

		toolHp.setStyleName("OccurrenceView-ToolBar");
		mainVp.add(toolHp);
		mapLink.setStyleName("link");
		listLink.setStyleName("link");
		uploadLink.setStyleName("link");

		switchViewPanel.setSpacing(5);
		mainSp.setWidget(mainVp);
		initWidget(mainSp);
		mainSp.addStyleName("OccurrenceView");
		mainVp.setCellHeight(toolHp, "20px");
		addHistoryItem(false);
		Window.addResizeHandler(this);
		History.addValueChangeHandler(this);
		String historyToken = History.getToken();
		if (historyToken.trim().equals("")) {
			switchView(DEFAULT_VIEW, true);
		} else {
			handleOnValueChange(historyToken);
		}
		mapLink.addClickHandler(this);
		listLink.addClickHandler(this);
		uploadLink.addClickHandler(this);

		DeferredCommand.addCommand(new Command() {
			public void execute() {
				resize(Window.getClientWidth(), Window.getClientHeight());
			}
		});
	}

	private final QueryFiltersMap queryFiltersMap;

	private class QueryFiltersMap {
		private final Map<String, Set<String>> typeFiltersMap = new TreeMap<String, Set<String>>();
		private final Map<String, String> historyTypeValue = new HashMap<String, String>();

		public QueryFiltersMap() {
			historyTypeValue.put(constants.AllPositivelyReviewed(),
					ALL_POS_REVIEWED);
			historyTypeValue.put(constants.AllNegativelyReviewed(),
					ALL_NEG_REVIEWED);
			historyTypeValue.put(constants.AllAwaitingReview(),
					ALL_AWAIT_REVIEW);
			historyTypeValue.put(constants.AllInvalid(), ALL_INVALID);
			historyTypeValue.put(constants.AllOccurrences(), ALL_OCC);

			historyTypeValue.put(constants.MyPositivelyReviewed(),
					MY_POS_REVIEWED);
			historyTypeValue.put(constants.MyNegativelyReviewed(),
					MY_NEG_REVIEWED);
			historyTypeValue.put(constants.MyValidated(), MY_AWAITING_REVIEW);
			historyTypeValue.put(constants.MyInvalidated(), MY_INVALID);
			historyTypeValue.put(constants.MyOccurrences(), MY_OCCURRENCES);
			historyTypeValue.put(constants.OccurrencesToReview(),
					OCCURRENCES_TO_REVIEW);
			historyTypeValue.put(constants.MyOverallPositivelyReviewed(),
					MY_OVERALL_POS_REVIEW);
			historyTypeValue.put(constants.MyOverallNegativelyReviewed(),
					MY_OVERALL_NEG_REVIEW);
		}

		void addFilters(String type, OccurrenceQuery query, User user) {
			String typeTokenValue = historyTypeValue.get(type);
			typeFiltersMap.put(typeTokenValue, query.getFiltersFromProperty(
					type, user, ResultFilter.PUBLIC));
		}

		String getType(Set<String> filters) {
			for (String type : typeFiltersMap.keySet()) {
				Set<String> f = typeFiltersMap.get(type);
				if (filters.equals(f)) {
					return type;
				}
			}
			for (String type : typeFiltersMap.keySet()) {
				Set<String> f = typeFiltersMap.get(type);
				if (filters.containsAll(f)) {
					return type;
				}
			}
			return null;
		}

		void init(User loggedinUser) {
			typeFiltersMap.clear();
			for (String type : historyTypeValue.keySet()) {
				String value = historyTypeValue.get(type);
				if (loggedinUser == null && value.startsWith("my")) {
					continue;
				}
				addFilters(type, query, loggedinUser);
			}
		}
	}

	private void initViews() {
		ViewInfo detailView = DetailView.init(this, this, query);
		viewInfos.put(DETAIL.toLowerCase(), detailView);
		ViewInfo mapView;
		mapView = MapView.init(this, query, this, true,
				(OccurrenceListener) detailView);
		viewInfos.put(MAP.toLowerCase(), mapView);
		ViewInfo listView = ListView.init(this, query, this,
				(OccurrenceListener) detailView);
		viewInfos.put(LIST.toLowerCase(), listView);
		//kayjean
		//ViewInfo advanceView = AdvanceSearchView.init(this, new Clickable() { 
		//	public void click() 
		//	{
		//		searchForm.searchBox.setText("");
		//		searchForm.search();
		//	}
		//});
		//viewInfos.put(ADVANCE.toLowerCase(), advanceView);

	}

	private int currentPageNum = 1;

	public void onPageLoaded(List<Occurrence> data, int pageNumber) {
		currentPageNum = pageNumber;
	}

	// misc 一些設定文字

	/**
	 * Constants history value for "All Occurrence" search selection
	 */
	private static final String ALL_OCC = "all occurrences";
	/**
	 * Constants history value for "All Vetted" search selection
	 */
	private static final String ALL_POS_REVIEWED = "all pos reviewed";
	/**
	 * Constants history value for "All Invalid" search selection
	 */
	private static final String ALL_INVALID = "all invalid";

	private static final String ALL_AWAIT_REVIEW = "all await review";

	/**
	 * Constants history value for "All Validated" search selection
	 */
	private static final String ALL_NEG_REVIEWED = "all neg reviewed";
	/**
	 * Constants history value for "All Validated Not Vetted" search selection
	 */
	private static final String ALL_VALIDATED_NOT_VETTED = "all validated not certify";

	/**
	 * Constants history value for "My Occurrence" search selection
	 */
	public static final String MY_POS_REVIEWED = "my pos reviewed";
	/**
	 * Constants history value for "My Vetted" search selection
	 */
	public static final String MY_NEG_REVIEWED = "my neg reviewed";
	/**
	 * Constants history value for "My Invalid" search selection
	 */
	private static final String MY_INVALID = "my invalid";
	/**
	 * Constants history value for "My Validated" search selection
	 */
	private static final String MY_AWAITING_REVIEW = "my awaiting review";

	private static final String MY_OVERALL_POS_REVIEW = "my overall pos review";
	private static final String MY_OVERALL_NEG_REVIEW = "my overall neg review";

	private static final String MY_OCCURRENCES = "my occurrences";
	/**
	 * Constants history value for "All Validated Not Vetted" search selection
	 */
	public static final String OCCURRENCES_TO_REVIEW = "occurrences to review";

	
	
	
	// 地圖拖動的時候,必須要通知occurrenceview將currentpagenum設回1. 這資訊沒有辦法用HISTORY通知, 也很難用event通知,只好很醜的加入一個function完成這個任務
	protected void resetpagenum(  String SOUTHWEST , String NORTHEAST  ){
		currentPageNum = 1;
		searchForm.setSOUTHWEST(SOUTHWEST);
		searchForm.setNORTHEAST(NORTHEAST);
	}

}
